/*
 * Created by IntelliJ IDEA.
 * User: amrk
 * Date: 10/06/2004
 * Time: 20:19:41
 */
package com.talios.jira.idea.intentions;

import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.IntentionManager;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.components.ProjectComponent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.search.TodoPattern;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.util.IncorrectOperationException;
import com.intellij.ide.todo.TodoConfiguration;
import com.talios.jira.browser.IssueCreation;
import com.talios.jira.idea.JiraConfigurationComponent;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @jira INTPLUG-98 Converted TODO entries should strip trailing ": " if following token
 */
public class JiraTODOIssueCreationIntention implements ProjectComponent, IntentionAction {
    private static final Logger LOGGER = Logger.getInstance("Jira Browser");

    private final Project myProject;
    private String todoToken;

    public static JiraTODOIssueCreationIntention getInstance(Project project) {
        return project.getComponent(JiraTODOIssueCreationIntention.class);
    }

    public JiraTODOIssueCreationIntention(Project project) {
        myProject = project;
    }

    public void projectOpened() {
    }

    public void projectClosed() {
    }

    public String getComponentName() {
        return "JiraTODOIssueCreationIntention";
    }

    public void initComponent() {
        IntentionManager.getInstance(myProject).addAction(this);
    }

    public void disposeComponent() {
    }

    public String getText() {
        return "Create a JIRA Issue for TODO entry";
    }

    public String getFamilyName() {
        return getText();
    }

    public boolean isAvailable(Project project, Editor editor, PsiFile file) {
        int offset = editor.getCaretModel().getOffset();
        final PsiElement element = file.findElementAt(offset);
        if (element == null) return false;
        if (!element.isWritable()) return false;

        // TODO This should handle all defined TODO types in IDEA
        if (element instanceof PsiComment) {
            final PsiComment token = (PsiComment) element;

            String todoRegex = getTodoRegex();
            LOGGER.info("todoRegex is: " + todoRegex);

            Matcher matcher = Pattern.compile(todoRegex).matcher(token.getText().toUpperCase());

            if (matcher.find() && token.getText().indexOf("@jira") == -1) {
                todoToken = matcher.group(1);
                return true;
            }
        }
        return false;
    }

    private String getTodoRegex() {
        TodoPattern[] patterns = TodoConfiguration.getInstance().getTodoPatterns();
        return getTodoRegexFromPatterns(patterns);
    }
    
    protected String getTodoRegexFromPatterns(TodoPattern[] patterns) {
        StringBuffer buf = new StringBuffer();
        buf.append("\\b(");
        for (int i = 0; i < patterns.length; i++) {
            String todoPattern = patterns[i].getPatternString().trim().toUpperCase();

            try {
                Pattern.compile(todoPattern);
                buf.append(todoPattern);
                if (i < patterns.length - 1) {
                    buf.append('|');
                }
            } catch (java.util.regex.PatternSyntaxException e) {
                LOGGER.warn("Invalid TODO pattern - ignoring");
            }

        }
        buf.append(")\\b");
        return buf.toString();
    }

    /**
     * @jira INTPLUG-98 Converted TODO entries should strip trailing ": " if following token
     */
    public void invoke(Project project, Editor editor, PsiFile psiFile) throws IncorrectOperationException {

        int offset = editor.getCaretModel().getOffset();
        final PsiElement element = psiFile.findElementAt(offset);
        final PsiComment token = (PsiComment) element;

        int todoIndex = token.getText().indexOf(todoToken);
        String title = token.getText().substring(todoIndex + todoToken.length()).trim();
        title = title.replaceFirst("\\:\\s", "");

        String env = psiFile.getVirtualFile().getName() + ':' + (editor.getCaretModel().getLogicalPosition().line + 1);

        IssueCreation issueCreation = new IssueCreation(project);
        issueCreation.getTitle().setText(title);
        issueCreation.getEnvironment().setText(env);
        final String issueKey = issueCreation.createIssue();

        if (issueKey != null) {
            annotateTodoComment(project, editor, token, issueKey);
            JiraConfigurationComponent config = project.getComponent(JiraConfigurationComponent.class);
            config.refreshIssues();
        }
    }

    private void annotateTodoComment(Project project, Editor editor, final PsiComment token, final String issueKey) {
        final Project aProject = project;
        final Editor aEditor = editor;

        // TODO @jira INTPLUG-84 After creating the issue, update the TODO item with the issue key.
        CommandProcessor.getInstance().executeCommand(project, new Runnable() {
            public void run() {
                ApplicationManager.getApplication().runWriteAction(new Runnable() {
                    public void run() {
                        try {
                            PsiDocumentManager pdm = PsiDocumentManager.getInstance(aProject);
                            pdm.commitDocument(aEditor.getDocument());
                            String newTokenText = token.getText().replaceFirst("TODO", "TODO @jira " + issueKey);
                            PsiManager psiManager = PsiManager.getInstance(aProject);
                            PsiElementFactory elementFactory = psiManager.getElementFactory();

                            PsiComment newElement = elementFactory.createCommentFromText(newTokenText, null);
                            PsiElement newToken = token.replace(newElement);

                            final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(aProject);
                            codeStyleManager.reformat(newToken);

                            pdm.commitDocument(aEditor.getDocument());

                        } catch (IncorrectOperationException e) {
                            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
                        }
                    }
                });
            }
        }, "Todo", null);
    }

    public boolean startInWriteAction() {
        return false;  //To change body of implemented methods use File | Settings | File Templates.
    }
}